﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;

using Effects.Effects;
using Effects.Wave;

namespace Effects.GUI
{
    /// <summary>
    ///  Displays content of <see cref="ChangesManager"/>
    /// </summary>
    /// <remarks>
    /// <para>
    ///  Displays changes in effect's settings recorded by <see cref="ChangesManager"/>. It also provides functionality to add new cahnges, remove and edit them.
    ///  Useful feature is also editing effect's initial settings.
    ///  </para>
    /// </remarks>
    public partial class ChangesManagerVisualizer : Form
    {
        /// <summary>
        ///  ChangesManager to visualize.
        /// </summary>
        private ChangesManager manager;

        /// <summary>
        ///  Array of effect's parameters.
        /// </summary>
        private EffectParam[] parameters;

        /// <summary>
        ///  Mapping from keys to names of parameters.
        /// </summary>
        private Dictionary<object, EffectParam> keyParams;

        /// <summary>
        ///  Finds param with given name of the key.
        /// </summary>
        /// <param name="keyName"> Name of the param's key </param>
        /// <returns> <see cref="EffectParam"/> with given name </returns>
        private EffectParam FindParam(string keyName)
        {
            foreach (object key in keyParams.Keys)
            {
                if (keyParams[key].Name == keyName)
                    return keyParams[key];
            }
            return new EffectParam();
        }

        /// <summary>
        ///  Initial settings of the effect.
        /// </summary>
        private object[] initials;

        /// <summary>
        ///   Finds value corresponding with given name.
        /// </summary>
        /// <param name="param"> <see cref="EffectParam"/> with List type to search </param>
        /// <param name="name"> Name of the value </param>
        /// <returns> Value corresponding with given name </returns>
        private object FindValueListType(EffectParam param,string name)
        {
            ListItem[] pairs = (ListItem[])param.Bounds;
            for (int i = 0; i < pairs.Length; i++)
                if(pairs[i].DisplayName == name)
                    return pairs[i].Value;
            return null;
        }

        /// <summary>
        ///  Sampling rate of input data
        /// </summary>
        private int sampleRate;

        /// <summary>
        ///  Gets or sets the sample to work with.
        /// </summary>
        public int SampleRate
        {
            get { return sampleRate; }
            set { sampleRate = value; }
        }

        /// <summary>
        ///  Channels count of input data.
        /// </summary>
        private int channels;

        /// <summary>
        ///  Gets or sets the number of channels to work with.
        /// </summary>
        public int Channels
        {
            get { return channels; }
            set { channels = value; }
        }

        /// <summary>
        ///  Creates new instance of visualiser/
        /// </summary>
        public ChangesManagerVisualizer()
        {
            InitializeComponent();
        }


        /// <summary>
        ///  Creates new instance of <see cref="ChangesManagerVisualizer"/> that visualizes given <see cref="ChangesManager"/> with paramaters
        /// </summary>
        /// <param name="manager"> <see cref="ChangesManager"/> to visualize </param>
        /// <param name="parameters"> Parameters of <see cref="global::Effects.Effects.Effect"/> managed by given manager </param>
        /// <param name="sampleRate"> Sampling rate of input signal. </param>
        /// <param name="channels"> Number of channels of input signal. </param>
        public ChangesManagerVisualizer(ChangesManager manager,EffectParam[] parameters,int sampleRate,int channels)
        {
            InitializeComponent();
            this.channels = channels;
            this.sampleRate = sampleRate;
            this.manager = manager;
            this.parameters = parameters;
            this.initials = (object[])manager.Initials;
            PrepareKeysMapping();
            Visualize();
        }

        /// <summary>
        ///  Prepares mapping between parameter keys and parameters.
        /// </summary>
        /// <remarks>
        ///  It fills <see cref="ChangesManagerVisualizer.keyParams"/> dictionary according to parameters.
        /// </remarks>
        private void PrepareKeysMapping()
        {
            keyParams = new Dictionary<object, EffectParam>();
            for (int i = 0; i < parameters.Length; i++)
                keyParams.Add(parameters[i].Key, parameters[i]);
        }


        /// <summary>
        ///  Visualises given list of changes. Fills <see cref="ChangesManagerVisualizer.ChangesListView"/> with appropriate values
        /// </summary>
        private void Visualize()
        {
            LinkedList<ChangesManager.Node> changes = manager.Changes;
            Clear();
            foreach (ChangesManager.Node node in changes.GetEnumerator())
            {
                EffectParam param = keyParams[node.change.Key];
               
                //ListViewItem item = new ListViewItem(Effects.Wave.TimeSampleConverter.SampleToString(node.time, sampleRate, channels));
                ListViewItemExtraContent item = new ListViewItemExtraContent(TimeSampleConverter.SampleToString(node.time, sampleRate, channels));
                item.Value = node.time;

                //item.SubItems.Add(node.time.ToString());
                item.SubItems.Add(param.Name);
                if (param.ValueType == ParameterType.List)
                {
                    ListItem[] pairs = (ListItem[])param.Bounds;
                    for (int i = 0; i < pairs.Length; i++)
                        if(pairs[i].Value.Equals(node.change.Value))
                            item.SubItems.Add(pairs[i].DisplayName);
                }
                else
                    item.SubItems.Add(node.change.Value.ToString());
                
                //item.SubItems.Add(keyParams[node.change.Key].
                this.ChangesListView.Items.Add(item);
            }
        }

        /// <summary>
        ///  Clear all changes
        /// </summary>
        private void Clear()
        {
            this.ChangesListView.Items.Clear();
        }

        /// <summary>
        ///  Adds change to the <see cref="ChangesManagerVisualizer.ChangesListView"/>
        /// </summary>
        /// <param name="timeD"> time of the change </param>
        /// <param name="key"> Key of the change </param>
        /// <param name="value"> Value of the change </param>
        private void AddChange(DateTime timeD, object key, object value)
        {
            EffectParam param = keyParams[key];
            //ListViewItem item = new ListViewItem(time.ToString());
            ListViewItemExtraContent item = new ListViewItemExtraContent(Wave.TimeSampleConverter.TimeToString(timeD));
            item.Value = Wave.TimeSampleConverter.TimeToSample(timeD, sampleRate, channels);

            int time = Wave.TimeSampleConverter.TimeToSample(timeD, sampleRate, channels);

            for (int i = 0; i < this.ChangesListView.Items.Count; i++)
            {
                //int changeTime = Convert.ToInt32(this.ChangesListView.Items[i].SubItems[0].Text);
                //int changeTime = Wave.TimeSampleConverter.StringToSample(this.ChangesListView.Items[i].SubItems[0].Text, sampleRate, channels);
                int changeTime = (int)((ListViewItemExtraContent)this.ChangesListView.Items[i]).Value;
                if (time < changeTime)
                {
                    //item.SubItems.Add(time.ToString());
                    item.SubItems.Add(param.Name);
                    if (param.ValueType == ParameterType.List)
                    {
                        ListItem[] pairs = (ListItem[])param.Bounds;

                        for (int j = 0; j < pairs.Length; j++)
                            if (pairs[j].Value.Equals(value))
                                item.SubItems.Add(pairs[j].DisplayName);
                    }
                    else
                        item.SubItems.Add(value.ToString());

                    this.ChangesListView.Items.Insert(i, item);
                    return;
                }
            }

            // add last
            //item.SubItems.Add(time.ToString());
            item.SubItems.Add(param.Name);
            if (param.ValueType == ParameterType.List)
            {
                ListItem[] pairs = (ListItem[])param.Bounds;

                for (int j = 0; j < pairs.Length; j++)
                    if (pairs[j].Value.Equals(value))
                        item.SubItems.Add(pairs[j].DisplayName);
            }
            else
                item.SubItems.Add(value.ToString());

            this.ChangesListView.Items.Add(item);
        }

        #region button_handlers
        /// <summary>
        ///  <see cref="ChangesManagerVisualizer.AddButton"/> click event handler
        /// </summary>
        /// <param name="sender"> Object that raised event </param>
        /// <param name="e"> Event arguments </param>
        private void AddButton_Click(object sender, EventArgs e)
        {
            // show dialog
            AddParamChangeForm addChange = new AddParamChangeForm();
            addChange.Parameters = this.parameters;
            addChange.ShowDialog();
            
            // if ok add new change.
            if (addChange.OK)
                AddChange(addChange.Time, addChange.Key, addChange.Value);
        }

        /// <summary>
        ///  <see cref="ChangesManagerVisualizer.EditButton"/> click event handler
        /// </summary>
        /// <param name="sender"> Object that raised event </param>
        /// <param name="e"> Event arguments </param>
        private void EditButton_Click(object sender, EventArgs e)
        {
            if(this.ChangesListView.SelectedItems.Count > 0)
            {
                ListViewItem item = this.ChangesListView.SelectedItems[0];
                EffectParam param = FindParam(item.SubItems[1].Text);

                DateTime time = Wave.TimeSampleConverter.StringToTime(item.SubItems[0].Text);
                object key = param.Key;
                object value=null;
                switch (param.ValueType)
                {
                    case ParameterType.Integer:
                        value = Convert.ToInt32(item.SubItems[2].Text);
                        break;
                    case ParameterType.Real:
                        value = Convert.ToDouble(item.SubItems[2].Text);
                        break;
                    case ParameterType.List:
                        value = FindValueListType(param,item.SubItems[2].Text);
                        break;
                }

                // show dialog
                AddParamChangeForm addChange = new AddParamChangeForm();
                addChange.Parameters = this.parameters;
                addChange.SetParam(key, value,time);
                addChange.Edit = true;
                addChange.ShowDialog();

                // if ok edit the value
                if (addChange.OK)
                {
                    switch (param.ValueType)
                    {
                        case ParameterType.Integer:
                            item.SubItems[2].Text = addChange.Value.ToString();
                            break;
                        case ParameterType.Real:
                            item.SubItems[2].Text = addChange.Value.ToString();
                            break;
                        case ParameterType.List:
                            ListItem[] pairs = (ListItem[])param.Bounds;
                            for (int i = 0; i < pairs.Length; i++)
                                if (pairs[i].Value.Equals(addChange.Value))
                                    item.SubItems[2].Text = pairs[i].DisplayName;
                            break;
                    }
                }
            }
        }

        /// <summary>
        ///  <see cref="ChangesManagerVisualizer.RemoveButton"/> click event handler
        /// </summary>
        /// <param name="sender"> Object that raised event </param>
        /// <param name="e"> Event arguments </param>
        private void RemoveButton_Click(object sender, EventArgs e)
        {
            // just remove selected item
            if(this.ChangesListView.SelectedItems.Count > 0)
                this.ChangesListView.Items.Remove(this.ChangesListView.SelectedItems[0]);
        }

        /// <summary>
        ///  <see cref="ChangesManagerVisualizer.CloseButton"/> click event handler
        /// </summary>
        /// <param name="sender"> Object that raised event </param>
        /// <param name="e"> Event arguments </param>
        private void CloseButton_Click(object sender, EventArgs e)
        {
            // create list and close
            LinkedList<ChangesManager.Node> list = new LinkedList<ChangesManager.Node>();
            foreach (ListViewItem item in this.ChangesListView.Items)
            {
                int time = Wave.TimeSampleConverter.StringToSample(item.SubItems[0].Text,sampleRate,channels);

                // key
                EffectParam param = FindParam(item.SubItems[1].Text);
                object key = param.Key;

                object value=null;
                switch (param.ValueType)
                {
                    case ParameterType.Integer:
                        value = Convert.ToInt32(item.SubItems[2].Text);
                        break;
                    case ParameterType.Real:
                        value = Convert.ToDouble(item.SubItems[2].Text);
                        break;
                    case ParameterType.List:
                        value = FindValueListType(param,item.SubItems[2].Text);
                        break;
                }

                EffectSettingsChangedEventArgs settings = new EffectSettingsChangedEventArgs(key, value);
                list.AddLast(new ChangesManager.Node(settings,time));                     
            }

            // assing the list
            this.manager.Changes = list;
            
            // assigning initials
            if(initials!=null)
                this.manager.Initials = initials;

            // close
            this.Close();
        }

        /// <summary>
        ///  <see cref="ChangesManagerVisualizer.EditInitialsButton"/> click event handler
        /// </summary>
        /// <param name="sender"> Object that raised event </param>
        /// <param name="e"> Event arguments </param>
        private void EditInitialsButton_Click(object sender, EventArgs e)
        {
            // show dialog
            AddParamChangeForm addChange = new AddParamChangeForm();
            addChange.Parameters = this.parameters;
            addChange.Values = initials;
            addChange.AllParams = true;
            addChange.ShowDialog();

            // if ok save initials
            if (addChange.OK)
                this.initials = addChange.Values;
        }

        #endregion
    }
}
